/**
 * UTSDK namespace
 * Can be used in php pages to call UTSDK javascript functions
 * 
 * VERSIONING:
 * v1.0 - Created UTSDK and added functions
 * v1.1 - Added UTSDK.VALIDATION
 * v1.2 - Included search.js and moved onload funtion to UTSDK
 * v1.3 - Updated fade and onload methods
 * v1.4 - Added UTSDK.AJAX and auto update form methods
 * v1.5 - Added ability to get all element with an id that matches a string
 * v1.6 - Added delete form method - Not complete
 * v1.7 - Moved json modification methods to UTSDK.AJAX
 * v1.8 - Added disable/enable method
 * v1.9 - Included UTSDK.CONSTANTS
 * v2.0 - Modified update form to set select form value defaults to a value rather
 *        than nothing using data-default
 * v2.1 - Added select update method
 * v2.2 - Updated updateform method to account for elements names not in form
 * v2.3 - Add history.back()
 * v2.4 - Add self url return and conversion of decimal to dollar
 * v2.5 - updateSelect function improvements
 * v2.6 - Change update form from post to get
 * v2.7 - Add method to return url parameters
 * v2.8 - Added timestamp method
 * v2.9 - Add inner text method
 */
var UTSDK = window.UTSDK || {};
UTSDK.VALIDATION = UTSDK.VALIDATION || {};
UTSDK.AJAX = UTSDK.AJAX || {};
UTSDK.CONSTANTS = UTSDK.CONSTANTS || {};
/**
 * Returns first element by the specified ID or an array of elements that match
 * To search for all matching elements place a * at the begining of the id
 * @param {string} id to search for
 * @returns {element|array} the element or elements matching the id
 */
UTSDK.$ = function(id) {
    if (id[0] === "*") {
        id = id.substring(1, id.length);
        var matches = [];
        var elems = document.getElementsByTagName("*");
        for (var i = 0; i < elems.length; i++) {
            if (elems[i].id.indexOf(id) >= 0)
                matches.push(elems[i]);
        }
        return matches;
    }
    else
        return document.getElementById(id);
};
/**
 * Includes the specified stylesheet into the html page
 * @param {string} stylesheet to be inserted into the page
 */
UTSDK.addStylesheet = function(stylesheet) {
    var link = document.createElement("link");
    link.media = "screen";
    link.href = "stylesheet/" + stylesheet;
    link.rel = "stylesheet";
    link.type = "text/css";
    var head = document.getElementsByTagName("head")[0];
    head.insertBefore(link, head.children[3]);
};
UTSDK.getTimestamp = function() {
    if (!Date.now()) {
        Date.now = function() {
            return new Date().getTime();
        };
    }
    return Date.now();
};
/**
 * Redirects to the specidied page
 * @param {string} page to be redirected too
 */
UTSDK.redirect = function(page) {
    window.location = page;
};
/**
 * Redirect back one page
 */
UTSDK.back = function() {
    window.history.back();
};
/**
 * Adds a class to the specified element
 * @param {element} element to add the class too
 * @param {string} className the name of the class to be added
 */
UTSDK.addClass = function(elem, className) {
    if (elem.classList)
        elem.classList.add(className);
    else
        elem.className += " " + className;
};
/**
 * Removes a class from the specified element
 * @param {element} element to remove the class from
 * @param {string} className the name of the class to be removed
 */
UTSDK.removeClass = function(elem, className) {
    if (elem.classList)
        elem.classList.remove(className);
    else {
        if (elem.className)
            elem.className = elem.className.replace(new RegExp('(?:^|\\s)' + className + '(?!|\\S)'), '');
        elem.className += " " + className;
    }
};
/**
 * Makes the secified element fade in from opacity 0 to 1
 * Sets the display type to block
 * @param {element} element to fade in
 * @param {double} time the time to fade in
 */
UTSDK.fadeIn = function(elem, time) {
    if (elem.style.display === "block")
        return;
    var op = 0.1; // initial opacity
    elem.style.opacity = 0;
    elem.style.display = 'block';
    var timer = setInterval(function() {
        if (op >= 1) {
            clearInterval(timer);
        }
        elem.style.opacity = op;
        op += op * 0.1;
    }, time);
};
/**
 * Makes the specified element fade out from opacity 1 to 0
 * Sets the display type to none
 * @param {element} element to fade out
 * @param {double} time to fade out
 */
UTSDK.fadeOut = function(elem, time) {
    if (elem.style.display === "none")
        return;
    var op = 1; // initial opacity
    var timer = setInterval(function() {
        if (op <= 0.1) {
            clearInterval(timer);
            elem.style.display = 'none';
        }
        elem.style.opacity = op;
        op -= op * 0.1;
    }, time);
};
/**
 * Takes in an element within a form and retrieves the form values from the 
 * database by ajax call to php.  The responce (JSON object) loads the values
 * into the respective fields using their name attribute to identify them
 * @param {json} json the parameters to send in the get request
 * @param {page} page the page to send the get request
 * @param {function} callback method to call when update form is complete
 */
UTSDK.updateForm = function(json, page, callback) {
    if (page == null)
        page = "update.php";
    UTSDK.AJAX.get(page, json, function(responce, data) {
        if (responce === "")
            return;
        var form = UTSDK.$(data.id);
        responce = JSON.parse(responce);
        for (var key in responce) {
            if (form.elements[key]) {
                form.elements[key].value = responce[key];
                if (UTSDK.equalsIgnoreCase(form.elements[key].tagName, "SELECT")
                        && responce[key] === "") {
                    form.elements[key].value = form.elements[key].getAttribute("data-default");
                }
                if (form.elements[key])
                    UTSDK.dispatchEvent(form.elements[key], "change");
                if (callback)
                    callback(data);
            }
        }
    }, true);
};
/**
 * This method takes a json and a select element and get requests an array of json
 * from the page specified.  It then inserts the json elements into the select as 
 * options.  If the select has an error section it will print a loading gif while
 * the options are being requested.
 * @param {json} the json to send to update page
 * @param {element} select to be updated
 * @param {string} page to be called
 * @param {function} callback called after method complete
 */
UTSDK.updateSelect = function(json, select, page, callback) {
    if (page == null)
        page = "update.php";
    var error = UTSDK.$(select.id + "Error");
    if (error)
        error.innerHTML = '<img src="images/ajax-loader.gif"/>';
    UTSDK.AJAX.get(page, json, function(responce, data) {
        responce = JSON.parse("[" + responce + "]");
        while (select.lastChild) {
            if (select.children.length === 1)
                break;
            select.removeChild(select.lastChild);
        }
        for (var i = 0; i < responce.length; i++) {
            var newChild = document.createElement("OPTION");
            for (var key in responce[i]) {
                if (key === "name")
                    newChild.innerHTML = responce[i][key];
                else
                    newChild.value = responce[i][key];
            }
            select.appendChild(newChild);
        }
        if (error) {
            UTSDK.fadeOut(error, 1);
            error.innerHTML = "";
            error.style.display = "block";
        }
        if (callback)
            callback(data);
    }, true);
};
/**
 * This method manually calls an the event specified on the element specified
 * @param {element} element to dispatch event on
 * @param {string} eventName of the event to dispatch
 */
UTSDK.dispatchEvent = function(element, eventName) {
    if (element !== null) {
        if ("createEvent" in document) {
            var evt = document.createEvent("HTMLEvents");
            evt.initEvent(eventName, true, false);
            element.dispatchEvent(evt);
            return true;
        }
        else {
            element.fireEvent("on" + eventName);
            return true;
        }
    }
    console.log("Error in UTSDK.dispatchEvent");
    return false;
};
/**
 * This method sets the inner text of the element specified
 * @param {type} text text to set in elem's inner text
 * @param {element} elem element to set inner text 
 */
UTSDK.setInnerText = function(elem, text) {
    if (text == null || elem == null) {
        console.log("text or elem is null in UTSDK.setInnerText");
    }
    if ("textContent" in elem) { //Check whether element has textContent property
        elem.textContent = text;
    }
    else {
        elem.innerText = text;
    }
};
/**
 * Searches an elements ancestors to find an element thats tag matches the tag
 * specified.  If there is no parent with the specified tag the null is returned
 * @param {element} element to find the parent off
 * @param {string} tag name of the parent to search for
 * @returns {element|null} the nearest parent with the specified tag or null
 */
UTSDK.getNearestParent = function(elem, tagName) {
    var parent = elem;
    while (parent !== window) {
        parent = parent.parentElement;
        if (UTSDK.equalsIgnoreCase(parent.tagName, tagName))
            return parent;
    }
    return null;
};
UTSDK.getChildByDataId = function(parent, name) {
    var current = parent;
    return searchChild(current, name);
    function searchChild(current, name) {
        if (current.getAttribute("data-id") === name)
            return current;
        if (current.children.length === 0)
            return null;
        for (var i = 0; i < current.children.length; i++) {
            var child = searchChild(current.children[i], name);
            if (child != null) {
                return child;
            }
        }
    }
};
/**
 * Returns true if the two strings match ignoring case.  Converts both strings to
 * lower case then compares.
 * @param {string} s1 - the first string to compare
 * @param {string} s2 - the second string to compare
 * @returns {Boolean} the comparison result
 */
UTSDK.equalsIgnoreCase = function(s1, s2) {
    return s1.toLowerCase() === s2.toLowerCase();
};
/**
 * Returns a boolean value if the specified element is hidden
 * @param {element} elem the element to find if hidden
 * @returns {Boolean} true if the element is hidden
 */
UTSDK.isHidden = function(elem) {
    return (elem.offsetParent === null);
};
/**
 * Disables an element
 * @param {element} element to disable
 */
UTSDK.disable = function(elem) {
    elem.disabled = true;
    UTSDK.addClass(elem, "disabled");
    elem.setAttribute("data-required", "false");
};
/**
 * Enables an element
 * @param {element} element to enable
 */
UTSDK.enable = function(elem) {
    elem.disabled = false;
    UTSDK.removeClass(elem, "disabled");
    elem.setAttribute("data-required", "true");
};
/**
 * Reverses a json object so that keys are values and values are keys.  
 * Makes no attempt to resolve keys with the same value
 * @param {json} json to reverse
 * @returns {json} the reversed json
 */
UTSDK.reverseJson = function(json) {
    var out = {};
    for (var key in json) {
        out[json[key]] = key;
    }
    return out;
};
/**
 * Converts a float to a dollar format string
 * @param {float} decimal number to be converted
 * @returns {String} decimal to dollar format $#.##
 */
UTSDK.toDollarFormat = function(decimal) {
    return "$" + parseFloat(decimal).toFixed(2);
};
/**
 * Returns a string of the current url
 * @returns {string} url - current url
 */
UTSDK.getCurrentUrl = function() {
    var url = window.location.pathname;
    url += window.location.search;
    return url;
};
/**
 * This method returns the paramater value of the param specified
 * @param {string} param to look for in url
 * @returns {string} value of parameter specified
 */
UTSDK.getUrlParam = function(param) {
    var params = {};
    if (location.search) {
        var parts = location.search.substring(1).split('&');

        for (var i = 0; i < parts.length; i++) {
            var nv = parts[i].split('=');
            if (!nv[0])
                continue;
            params[nv[0]] = nv[1] || true;
        }
    }
    return params[param];
};
/**
 * Returns the relative path from the root directory
 * TODO: check this works under a real website
 * @returns {String} the relative path from the root
 */
UTSDK.getRelativeUrl = function() {
    var urlpath = window.location.pathname;
    var array = urlpath.split("/");
    if (array[1] === "sdp-spring-2014")
        return "/" + array[1] + "/";
    else
        return "";
};
/**
 * Include javascript classes here
 */
var url = UTSDK.getRelativeUrl();
include(url + "javascript/constants.js");
include(url + "javascript/validation.js");
include(url + "javascript/ajax.js");
include(url + "javascript/calendar.js");
include(url + "javascript/popup.js");
include(url + "javascript/search.js");
function include(filename) {
    // http://kevin.vanzonneveld.net
    // +   original by: mdsjack (http://www.mdsjack.bo.it)
    // +   improved by: Legaev Andrey
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +   improved by: Michael White (http://getsprink.com)
    // +      input by: Brett Zamir (http://brett-zamir.me)
    // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +      bugfixed by: Brett Zamir (http://brett-zamir.me)
    // %        note 1: Force Javascript execution to pause until the file is loaded. Usually causes failure if the file never loads. ( Use sparingly! )
    // %        note 2: The included file does not come available until a second script block, so typically use this in the header.
    // %        note 3: Uses global: php_js to keep track of included files
    // *     example 1: include('http://www.phpjs.org/js/phpjs/_supporters/pj_test_supportfile_2.js');
    // *     returns 1: 1
    var d = this.window.document;
    var isXML = d.documentElement.nodeName !== 'HTML' || !d.write; // Latter is for silly comprehensiveness
    var js = d.createElementNS && isXML ? d.createElementNS('http://www.w3.org/1999/xhtml', 'script') : d.createElement('script');
    js.setAttribute('type', 'text/javascript');
    js.setAttribute('src', filename);
    js.setAttribute('defer', 'defer');
    d.getElementsByTagNameNS && isXML ? (d.getElementsByTagNameNS('http://www.w3.org/1999/xhtml', 'head')[0] ? d.getElementsByTagNameNS('http://www.w3.org/1999/xhtml', 'head')[0].appendChild(js) : d.documentElement.insertBefore(js, d.documentElement.firstChild) // in case of XUL
            ) : d.getElementsByTagName('head')[0].appendChild(js);
    // save include state for reference by include_once
    var cur_file = {};
    cur_file[this.window.location.href] = 1;
    // BEGIN REDUNDANT
    this.php_js = this.php_js || {};
    // END REDUNDANT
    if (!this.php_js.includes) {
        this.php_js.includes = cur_file;
    }
    if (!this.php_js.includes[filename]) {
        this.php_js.includes[filename] = 1;
    } else {
        this.php_js.includes[filename]++;
    }

    return this.php_js.includes[filename];
}


